const fs = require('fs');
const path = require('path');
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const babel = require('@babel/core')

// 分析单独的模块
function getModuleInfo(file) {
    const body = fs.readFileSync(file, 'utf-8');
    // console.log('body',body)
    // 1.转换AST语法树
    // codeString => 对象 => 对象遍历解析
    // 编译的三步骤 parser traverse render
    const ast = parser.parse(body, {
        sourceType: 'module',// 使用es6的module
    })
    // console.log('ast:\n', ast)

    const deps = {};
    // 2.遍历抽象语法书ast的节点 =》traverse
    traverse(ast, {
        ImportDeclaration({ node }) {
            // 遇到import节点的时候触发
            // console.log('import:\n', node);
            // 收集依赖
            //node.source.value => add.js 这是个相对路径，我们要转化成绝对路径
            const dirname = path.dirname(file);
            // console.log(dirname,__dirname) // 前者是针对于该文件夹下环境的，后者是这对于整个环境的
            // 相对于入口文件的位置,window下\，mac/,路径兼容问题要处理一下
            // 没那么简单，真实项目中，import的文件可能是alias别名的，没有补全路径的，node_modules的，还有循环引用问题
            const astpath = './' + path.join(dirname, node.source.value).replace(/\\/g, '/');
            // console.log(astpath) 
            deps[node.source.value] = astpath;
        }
    })

    // 3.es6 => es5 编译后的代码
    const { code } = babel.transformFromAst(ast, null, {
        presets: ['@babel/preset-env']
    });
    console.log(code);

    const moduleInfo = { file, deps, code };
    // console.log(moduleInfo)
    return moduleInfo
}

// 获取依赖
// 递归调用 -》引用的资源还会引用其他的资源
function getDeps(temp, { deps }) {
    Object.keys(deps).forEach(key => {
        const child = getModuleInfo(deps[key]);
        temp.push(child);
        getDeps(temp, child)
    });
}

// 解析模块
function parseModules(file) {
    const moduleInfo = getModuleInfo(file);
    const temp = [moduleInfo];
    const depsGraph = {};

    getDeps(temp, moduleInfo);

    temp.forEach(info => {
        depsGraph[info.file] = {
            deps: info.deps,
            code: info.code,
        }
    })

    return depsGraph
}

// test
// const content = parseModules('./src/index.js');
// console.clear()
// console.log('---------------')
// console.log(content)

function bundle(file) {
    const depsString = JSON.stringify(parseModules(file));

    return `
    (function (list) {
        
        function require(file){
            // 路径不能用之前代码里的，es6打包后路径不会帮你更改
            function __require(path){
                return require(list[file].deps[path]);
            }
            var exports = {};
            (function(exports,code,require){
                eval(code)
            })(exports,list[file].code,__require)
            return exports;
        }
    
        // 这里就是为什么需要entry
        require('${file}')
    })(${depsString})
    `
}

const content = bundle('./src/index.js')

// 判断是否有dist目录，没有的话创建
!fs.existsSync('./dist') && fs.mkdirSync("./dist");
// 写入bundle.js
fs.writeFileSync('./dist/bundle.js', content)


